unit BlueDiamondUtilV1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls,
  Vcl.Imaging.pngimage, System.Actions, Vcl.ActnList, Vcl.Touch.GestureMgr,
  CPortCtl, CPort, Vcl.ComCtrls, BD_LabJack, Vcl.Grids, BD_UUT, BD_TestHead,
  Vcl.Menus, ksTimers, IDGlobal;



type
  TfrmMain = class(TForm)
    AppBar: TPanel;
    CloseButton: TImage;
    sbStatus: TStatusBar;
    Label1: TLabel;
    Memo1: TRichEdit;
    sgADData: TStringGrid;
    rgMode: TRadioGroup;
    sgSN: TStringGrid;
    Label4: TLabel;
    Label3: TLabel;
    tmrADRead: TTimer;
    btnClearData: TButton;
    pnlCal: TPanel;
    btnCaptureZero: TButton;
    btnCaptureMax: TButton;
    btnSendParams: TButton;
    pnlTest: TPanel;
    btnLedOn: TButton;
    btnSegsOn: TButton;
    btnSegsOff: TButton;
    btnLedOff: TButton;
    btnReadAD: TButton;
    btnReadADCont: TButton;
    btnExitCal: TButton;
    btnReadSN: TButton;
    btnSetSN: TButton;
    btnCal: TButton;
    btnReset: TButton;
    Label5: TLabel;
    Label6: TLabel;
    lblADReading: TLabel;
    pnlPressure: TPanel;
    cbFastest: TCheckBox;
    cbSeal: TCheckBox;
    cbVent: TCheckBox;
    cbHighPress: TCheckBox;
    ComFP5: TComPort;
    FP5Term: TComTerminal;
    Label7: TLabel;
    btnAutoCal: TButton;
    Label8: TLabel;
    Button1: TButton;
    btnLeakTest: TButton;
    tmrGP: TTimer;
    Panel1: TPanel;
    lblPress: TLabel;
    Label9: TLabel;
    lblLeakRate: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    btnGetPress: TButton;
    pbLeakTime: TProgressBar;
    btnProgram: TButton;
    Label2: TLabel;
    lblDecayVal: TLabel;
    pnlBattery: TPanel;
    Label12: TLabel;
    lblBattV: TLabel;
    Label13: TLabel;
    btnGetBattV: TButton;
    MainMenu1: TMainMenu;
    Settings1: TMenuItem;
    FP5COMSettings1: TMenuItem;
    ComTmr: TksTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    //procedure ComPort1RxChar(Sender: TObject; Count: Integer);
    procedure StrToBytes(AStr: string; var rVal: TByteArr);
    procedure CalEnterCal;
    function ScrollRichEditToBottom(var RichEdit: TRichEdit): Boolean;
    procedure btnReadADClick(Sender: TObject);
    procedure rgModeClick(Sender: TObject);
    procedure ResetUUT;
    procedure CalSendParams;
    procedure CalSetSN;
    procedure CalReadSN;
    procedure CalExitCal;
    procedure btnReadADContClick(Sender: TObject);
    procedure tmrADReadTimer(Sender: TObject);
    procedure btnClearDataClick(Sender: TObject);
    procedure CalCaptureZero;
    procedure CalCaptureMax;
    procedure btnSegsOffClick(Sender: TObject);
    procedure btnSegsOnClick(Sender: TObject);
    procedure btnLedOnClick(Sender: TObject);
    procedure btnLedOffClick(Sender: TObject);
    procedure adCaptureMax;
    procedure adCaptureZero;
    procedure adCaptureRtZ;
    procedure adCaptureOnce;
    procedure btnGetPressClick(Sender: TObject);
    procedure cbFastestClick(Sender: TObject);
    procedure cbVentClick(Sender: TObject);
    procedure cbSealClick(Sender: TObject);
    procedure cbHighPressClick(Sender: TObject);
    procedure btnProgramClick(Sender: TObject);
    procedure CalAutoCal;
    procedure GetUUTPressure;     //Reads the pressure applied to the UUT
    procedure ProcessCalBtns(Sender: TObject);
    procedure CalLeakTest;
    procedure tmrGPTimer(Sender: TObject);
    procedure btnGetBattVClick(Sender: TObject);
    procedure GetBattV;
    procedure FP5COMSettings1Click(Sender: TObject);
    procedure ComTmrTimer(Sender: TObject);
    procedure UpdateRx(rx: array of byte; len: Integer);
    procedure rxEnterCal;     //Communicate with UUT to enter calibration- exchange PW.
    procedure rxCaptureAD;  // Obtain A/D data from the UUT via LJ com port.
    procedure rxSendSN;     //Compute and send SN to the UUT via LJ comport.
    procedure rxReceiveSN;   //Request the SN from the UUT and receive via LJ comport.
    procedure rxSendParams;  //Send calibration parameters to the UUT via LJ comport.
    procedure rxExitCal;     //Tell UUT to exit cal sequence and power down.
    procedure rxDispUtil;    //LCD/LED display utilities
  private
    { Private declarations }
   
  public
    { Public declarations }
    ReadsCounter: Integer;
    AutoCal: Boolean;
  end;

var
  frmMain: TfrmMain;  LJ: TLabJack;  UUT: TUUT; TH: TTestHead;

implementation

{$R *.dfm}


(*******************************************************************************
 btnLedOff: Turn LCD backlight off
*******************************************************************************)
procedure TfrmMain.btnLedOffClick(Sender: TObject);
begin
  UUT.SendCmd(csLedOff, TIM_LCD, 3, [CAL_LEDOFF]);
end;

(*******************************************************************************
 btnLedOn: Turn LCD backlight on
*******************************************************************************)
procedure TfrmMain.btnLedOnClick(Sender: TObject);
begin
 UUT.SendCmd(csLedOn, TIM_LCD, 3, [CAL_LEDON]);
end;

(*******************************************************************************
 Program: Causes the FP5 Programmer to erase, program, and verify the target
          device.
*******************************************************************************)
procedure TfrmMain.btnProgramClick(Sender: TObject);
begin
  if rgMode.ItemIndex <> 0 then    //ensure in programming mode
    begin
      rgMode.ItemIndex := 0;
      Sleep(200);         //allow relays time to change over
    end;
  ComFP5.WriteStr('ep'+#10+#13);    //complete programming
end;

(*******************************************************************************
 btnSegsOff: Turn all LCD segments off
*******************************************************************************)
procedure TfrmMain.btnSegsOffClick(Sender: TObject);
begin
UUT.SendCmd(csLcdOff, TIM_LCD, 3, [CAL_LCDOFF]);
end;
(*******************************************************************************
 btnSegsOn: Turn on all LCD segments.
*******************************************************************************)
procedure TfrmMain.btnSegsOnClick(Sender: TObject);
begin
UUT.SendCmd(csLcdOn, TIM_LCD, 3, [CAL_LCDON]);
end;
(*******************************************************************************
btnClearData: Clears all data from the memo
*******************************************************************************)
procedure TfrmMain.btnClearDataClick(Sender: TObject);
begin
  Memo1.Lines.Clear;
end;

(*******************************************************************************
 btnReadADClick: Configures the RX state machine to look for  A to D data from
                 the UUT.
*******************************************************************************)
procedure TfrmMain.btnReadADClick(Sender: TObject);
var
  i: Integer;
begin
  ReadsCounter := 0;
  UUT.NumReadsRequired := 1;
  for i := 0 to 27 do
    UUT.ADArrayAvg[i] := 0;
  UUT.SendCmd(csCaptureAD, TIM_SEND_AD, 5, [CAL_SEND_AD]);
end;
(*******************************************************************************
 btnReadADClickCont: Enables timer to continuously configures the RX state
                     machine to look for  A to D data from the UUT.
*******************************************************************************)
procedure TfrmMain.btnReadADContClick(Sender: TObject);
const
  {$J+}
  BtnDown: Boolean = False;
  {$J-}
begin
  if BtnDown then
    begin
      BtnDown := False;
      tmrADRead.Enabled := False;
      lblADReading.Visible := False;
    end
    else
      begin
        BtnDown := True;
        tmrADRead.Enabled := True;
      end;
end;


(*******************************************************************************
 btnFastestClick: Toggle Fastest Head
*******************************************************************************)
procedure TfrmMain.cbFastestClick(Sender: TObject);
begin
  cbFastest.Checked := TH.SetFastest(cbFastest.Checked);
end;
(*******************************************************************************
 btnHighPressClick: Toggle High/Low pressure solenoid
*******************************************************************************)
procedure TfrmMain.cbHighPressClick(Sender: TObject);
begin
  cbHighPress.Checked := TH.SetHighLow(cbHighPress.Checked);
end;
                                            
(*******************************************************************************
 btnFastestClick: Toggle Seal solenoid
*******************************************************************************)
procedure TfrmMain.cbSealClick(Sender: TObject);
begin
  cbSeal.Checked := TH.SetSeal(cbSeal.Checked);
end;

(*******************************************************************************
 btnVentClick: Toggle Vent solenoid
*******************************************************************************)
procedure TfrmMain.cbVentClick(Sender: TObject);
begin
  cbVent.Checked := TH.SetVent(cbVent.Checked);
end;
 
(*******************************************************************************
 btnGetPressClick: Read the pressure from the Sensotec and display on GUI
*******************************************************************************)
procedure TfrmMain.btnGetPressClick(Sender: TObject);
begin
  GetUUTPressure;
end;

(*******************************************************************************
 btnGetBattVClick: Read the Battery voltage and display on GUI
*******************************************************************************)
procedure TfrmMain.btnGetBattVClick(Sender: TObject);
begin
  GetBattV;
end;

(*******************************************************************************
 GetUUTPressure: Read the pressure from the Sensotec and Display.
 ******************************************************************************)
procedure TfrmMain.GetUUTPressure;
var
  tmp: Double; i: Integer;
begin
  UUT.AppliedPressure := 0;
  for i := 1 to 10 do
    begin
      LJ.GetAnalogInput(1,tmp);
      UUT.AppliedPressure := UUT.AppliedPressure + tmp;
      Sleep(1);
    end;
  UUT.AppliedPressure := UUT.AppliedPressure/10;      //get average reading
  UUT.AppliedPressure := UUT.AppliedPressure*100;
  lblPress.Caption := FloatToStrF(UUT.AppliedPressure, ffFixed, 15,1) +
                      ' PSI / ' + FloatToStrF(UUT.AppliedPressure/14.69595, ffFixed, 15, 1) +
                      ' ATM';
end;

(*******************************************************************************
 GetBattVClick: Read the Battery voltage and display on GUI. Takes 10 readings,
                calculates average, and displays to two decimal places.
*******************************************************************************)
procedure TfrmMain.GetBattV;
var
  tmp: Double; i: Integer;
begin
  UUT.BattV := 0;
  for i := 1 to 10 do
    begin
      LJ.GetAnalogInput(0,tmp);
      UUT.BattV := UUT.BattV + tmp;
      Sleep(1);
    end;
  UUT.BattV := UUT.BattV/10;      //get average reading
  lblBattV.Visible := True;
  lblBattV.Caption := FloatToStrF(UUT.BattV, ffFixed, 15,2) + ' V';
end;

(*******************************************************************************
 FormClose: Performs clean up actions when form is closed (and application is
             exited).
*******************************************************************************)
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  //Turn solenoids off
  cbHighPress.Checked := False;
  cbVent.Checked := False;
  cbSeal.Checked := False;
  cbFastest.Checked := False;
  //Free objects and release mem
  LJ.Free;
  TH.Free;
  UUT.Free;
  LJ.CloseLabJack;
end;

(*******************************************************************************
 FormCreate: Performs initialization tasks when application is first started
             at time that form is created.
*******************************************************************************)
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  LJ := TLabJack.Create;
  TH := TTestHead.Create;
  UUT := TUUT.Create;
  UUT.InitParams;
  try
     LJ.InitLabJack;
  except
    ShowMessage('Error occured while connecting LabJack or opening ComPort.');
  end;
  if FileExists(ExtractFilePath(Application.ExeName)+'FP5ComSettings.ini') then
    try
      ComFP5.LoadSettings(stIniFile, ExtractFilePath(Application.ExeName)+'FP5ComSettings.ini' );
      ComFP5.Open;
    except
      ShowMessage('Unable to load FP5ComSettings.ini. Unable to open port.');
    end;
  //+++++++Need to load these next two arrays with data from files- not hard coded
  FillADDataConsts(sgADData);
  FillSNConsts(sgSN);
  //Initialize UUT parameters
  UUT.UUTPendingReset := True;
  UUT.WaitForAD := False;
  UUT.RcvSNFromUUT := False;
  UUT.WaitForSN := False;
  UUT.WaitForCal := False;
  TH.Intialize;
end;

(*******************************************************************************
 FO5COMSettings: Set the COM Port settings for the FP5 Pgmr. User will need
                 to know which port is connected to the Pgmr, using control panel.
******************************************************************************)
procedure TfrmMain.FP5COMSettings1Click(Sender: TObject);
var
  fp: string;
begin
  ShowMessage('FP5 Programmer should be configured for 9600, 8-N-1, '+#13 +
              'Flow Control = Hardware' +
              'on the appropriate COM port.');
  fp := ExtractFilePath(Application.ExeName);
  ComFP5.ShowSetupDialog;
  ComFP5.StoreSettings(stIniFile, fp+'FP5ComSettings.ini');
  if not ComFP5.Connected then
    try
      ComFP5.Open;
    except
      ShowMessage('COM PORT' + ComFP5.Port +' could not be opened.');
    end;
end;

(*******************************************************************************
 rgModeClick: Configure relays to connect to serial port (instead of NEC
              programmer) to allow serial port comms with UUT.
*******************************************************************************)
procedure TfrmMain.rgModeClick(Sender: TObject);
begin
  if rgMode.ItemIndex = 1 then
    begin
      //++++++Need to update with constants
      LJ.PutBit(4,1);
      sleep(50);
      LJ.PutBit(5,1);
      sleep(50);
      LJ.PutBit(7,1);
      pnlCal.Enabled := True;
      pnlTest.Enabled := True;
    end
    else
      begin
        LJ.PutBit(4,0);
        sleep(50);
        LJ.PutBit(5,0);
        sleep(50);
        LJ.PutBit(7,0);
        pnlCal.Enabled := False;
        pnlTest.Enabled := False;
    end
end;


(*******************************************************************************
 StrToBytes: Converts a text string of hex digits into an array of bytes.
*******************************************************************************)
procedure TfrmMain.StrToBytes(AStr: string; var rVal: TByteArr);
var
  bVal: TByteArr;
  hNib: byte;
  j, i: Integer;
  IsOdd: Boolean;
begin
  try
      IsOdd := Odd(Length(AStr));
       if IsOdd then
        AStr := '0'+AStr;               //make the tmp an even length (add leading 0 if necessary) so cann for full byte
      SetLength(bVal, Length(AStr));
      SetLength(rVal, Length(bVal) div 2);    //rVal holds the packed byte- half the length of AStr
      for j := 1 to Length(AStr)do
        begin
          if (ord(AStr[j])) <= $39 then        //'9' is 57 in ASCII
           bVal[j-1] := byte(AStr[j]) - $30      //subtract 48 from ASCII numeral equivalents
           else
            bVal[j-1] := byte(AStr[j]) - $37;    //subtract 55 from A-F chars (hex numbers)
         end;
      i := 0;
      j := 0;
      while j <= Length(bVal)-1 do
        begin
          hNib := (bVal[j] shl 4);         //shift hNib to upper 4 bits.
          inc(j);
          rVal[i] := (hNib or bVal[j]);
          inc(j);
          inc(i);
        end;
  except
    ShowMessage('Invalid number.');
  end;
end;
(*******************************************************************************
 tmrADRead: Configures the RX state machine to continousuly look for  A to D
            data from the UUT.
*******************************************************************************)
procedure TfrmMain.tmrADReadTimer(Sender: TObject);
begin
  if lblADReading.Visible = False then
    lblADReading.Visible := True
    else
      lblADReading.Visible := False;
  if not UUT.WaitForAd then
    btnReadAD.Click;
end;

(*******************************************************************************
 tmrGPRead: Timer fires during leak test to measure pressure decay
*******************************************************************************)
procedure TfrmMain.tmrGPTimer(Sender: TObject);
begin
  GetUUTPressure;     //Read the current pressure applied to the UUT
  if (UUT.LeakCnt>1) and (UUT.LeakCnt mod 10 = 0) then   //Step the leak test time progress bar
    pbLeakTime.StepIt;
  lblDecayVal.Caption := 'Decay Val: '+FloatToStrF((UUT.LeakTestStartPress-UUT.AppliedPressure),ffFixed, 15,1) + ' PSI';
  //IF the Leak test exceeds the maximum leak rate...
  if (UUT.LeakTestStartPress - UUT.AppliedPressure) > CAL_LEAK_RATE_MAX  then
    begin
      tmrGP.Enabled := False;
      cbVent.Checked := False;    //Allow to vent
      cbSeal.Checked := False;    //Release seal on syringe
      TH.SetFail(True);
      TH.SetPass(False);
      TH.SetTIP(False);
      ShowMessage('Leak test failure- leak rate:  '+FloatToStrF((UUT.LeakTestStartPress-UUT.AppliedPressure),ffFixed, 15,1) + ' PSI');
      lblLeakRate.Visible := False;
      lblDecayVal.Visible := False;
    end
    else
    //If the leak test  has not failed so far
      begin
        inc(UUT.LeakCnt);
        if UUT.LeakCnt > CAL_LEAK_TIME_TOT then     //If the leak test required time has elapsed.
          begin
            pbLeakTime.Visible := False;
            tmrGP.Enabled := False;
            cbSeal.Checked := False;    //Release seal on syringe
            cbVent.Checked := False;    //Allow to vent
            Memo1.Lines.Add('Leak Rate: ' + FloatToStrF((UUT.LeakTestStartPress-UUT.AppliedPressure),ffFixed, 15,1) + ' PSI');
            if AutoCal then
            begin
              CalEnterCal;
            end
              else
                begin
                  TH.SetFail(False);
                  TH.SetPass(True);
                end;
            lblLeakRate.Visible := False;
            lblDecayVal.Visible := False;
          end;
      end;
end;


(*******************************************************************************
 ScrollRichEditToBottom: This function auto scrolls the referenced RichEdit
                         when text is added that would cause it to fill the
                         RichEdit to the point that it needs to scroll.
******************************************************************************)
function TfrmMain.ScrollRichEditToBottom(var RichEdit: TRichEdit): Boolean;
var
  l: Integer;
begin
  Result := true;
  l := Length(RichEdit.Text);
  try
    RichEdit.SelStart := l;
    RichEdit.Perform(EM_SCROLLCARET, 0, 0);
  except
    try
      RichEdit.SelStart := l;
      SendMessage(RichEdit.Handle, EM_SCROLLCARET, 0, 0);
    except
      Result := False;
    end;
  end;
end;

(*******************************************************************************
 UpdateRx: Update the rx/tx data memo with information currently in the LJ rx
           buffer.
********************************************************************************)
procedure TfrmMain.UpdateRx(rx: array of byte; len: Integer);
var
 i: Integer; str: string;
begin
  str := 'RxCnt: '+IntToStr(len) + ', RX<- ';
  for i := 0 to len-1 do    //show the RX Data in the memo.
    str := str + ByteToHex(LJ.RxData[i]) + ' ';
  Memo1.Lines.Add(str);
  //Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
end;
(*******************************************************************************
 ProcessCalBtns: Read the id of the cal panel button that was pressed, and set
                 up the RX state machine accordingly
 ******************************************************************************)
procedure TfrmMain.ProcessCalBtns(Sender: TObject);
begin
  case TButton(Sender).Tag of
    0: CalAutoCal;
    1: ResetUUT;
    2: CalEnterCal;
    3: CalSetSN;
    4: CalReadSN;
    5: begin
         UUT.CaptureZero := True;   //must set here because CalCaptureZero is shared with RTZ capture.
         CalCaptureZero;
       end;
    6: CalCaptureMax;
    7: CalSendParams;
    8: CalExitCal;
    9: begin
         UUT.CaptureRtz := True;
         CalCaptureZero;
       end;
    10: CalLeakTest;
  end;
end;
(*******************************************************************************
 ResetUUT: Issues reset to the UUT.
*******************************************************************************)
procedure TfrmMain.ResetUUT;
begin
  UUT.UUTPendingReset := True;
  LJ.PutBit(6,0);
  sleep(20);
  LJ.PutBit(6,1);
  //At this point, the UUT should send CAL_UUTACTIVE to the UART. Need to verify.
  sleep(500);     //Give the UUT a 500 mS to respond. This is a min requirment, so don't change.
  LJ.ReadUART;   //Check to UART
  if LJ.iRxCnt >0 then
    if LJ.RxData[LJ.iRxCnt -1] = CAL_UUTACTIVE then
      begin
        UUT.UUTPendingReset := False;
        sbStatus.Panels[1].Text := 'UUT Active'
      end
      else
        begin
          UUT.UUTPendingReset := True;
          sbStatus.Panels[1].Text := 'UUT Not Active'
        end;
end;

(*******************************************************************************
 adCaptureMax: find average of 15 readings taken at max pressure.
*******************************************************************************)
procedure TfrmMain.adCaptureMax;
var
  i: Integer;
 begin
   Memo1.Lines.Add('Entered adCaptureMax.');
   for i := 0 to 27 do
     begin
       UUT.ADArrayAvg[i] := UUT.ADArrayAvg[i] div UUT.NumReadsRequired;
       UUT.ADArray[i] := byte(UUT.ADArrayAvg[i]);
     end;
     UUT.GetAvgVals(UUT.MaxVals);
     UUT.CaptureMax := False;
     Memo1.Lines.Add('***********  Max Pressure Averages  *************');
     Memo1.Lines.Add('Current Pressure average: '+ IntToStr(UUT.MaxVals.PresAD));
     Memo1.Lines.Add('Max Pressure average: ' + IntToStr(UUT.MaxVals.PresADMax));
     Memo1.Lines.Add('Min Pressure average: ' + IntToStr(UUT.MaxVals.PresADMin));
     Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
end;

(*******************************************************************************
 adCaptureZero: find average of 15 readings taken at zero pressure.
*******************************************************************************)
 procedure TfrmMain.adCaptureZero;
var
  i: Integer;
 begin
   Memo1.Lines.Add('Entered adCaptureZero.');
   for i := 0 to 27 do
     begin
       UUT.ADArrayAvg[i] := UUT.ADArrayAvg[i] div UUT.NumReadsRequired;
       UUT.ADArray[i] := byte(UUT.ADArrayAvg[i]);
     end;
     UUT.GetAvgVals(UUT.ZeroVals);
     UUT.CaptureZero := False;
     Memo1.Lines.Add('***********  Zero Pressure Averages  *************');
     Memo1.Lines.Add('Current Pressure average: '+ IntToStr(UUT.ZeroVals.PresAD));
     Memo1.Lines.Add('Max Pressure average: ' + IntToStr(UUT.ZeroVals.PresADMax));
     Memo1.Lines.Add('Min Pressure average: ' + IntToStr(UUT.ZeroVals.PresADMin));
     Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
end;
 (*******************************************************************************
 adCaptureRtZ: find average of 15 readings taken at zero pressure after having
               been at full pressure.
*******************************************************************************)
 procedure TfrmMain.adCaptureRtZ;
var
  i: Integer;
 begin
   Memo1.Lines.Add('Entered adCaptureRtZ.');
   //Display new data in left most column.
   for i := 0 to 27 do
     begin
       UUT.ADArrayAvg[i] := UUT.ADArrayAvg[i] div UUT.NumReadsRequired;
       UUT.ADArray[i] := byte(UUT.ADArrayAvg[i]);
     end;
     UUT.GetAvgVals(UUT.RTZVals);
     UUT.CaptureRtZ := False;
     Memo1.Lines.Add('***********  Return to Zero Pressure Averages  *************');
     Memo1.Lines.Add('Current Pressure average: '+ IntToStr(UUT.RtZVals.PresAD));
     Memo1.Lines.Add('Max Pressure average: ' + IntToStr(UUT.RtZVals.PresADMax));
     Memo1.Lines.Add('Min Pressure average: ' + IntToStr(UUT.RtZVals.PresADMin));
     Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
 end;


 (*******************************************************************************
 adCaptureOnce: Display a single reading of the A/D value
*******************************************************************************)
 procedure TfrmMain.adCaptureOnce;
begin
  UUT.CaptureOnce := False;
end;


 (*******************************************************************************
 CalLeakTest: Perform leak test at 440
*******************************************************************************)
procedure TfrmMain.CalLeakTest;
var
  i: Integer;
begin
 cbFastest.Checked := True;
 cbVent.Checked := True;
 Sleep(50);
 cbHighPress.Checked := True;
 Memo1.Lines.Add('Pressuring UUT to 440 PSI.');
 Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
 Sleep(5000);    //need 5 seconds to pressurize
  i := 0;
  UUT.AppliedPressure := 0;  //sent to initial value to force loop to run.
  while((i<5) and ((UUT.AppliedPressure >441.0) or (UUT.AppliedPressure < 439.0))) do
    begin
      GetUUTPressure;
      Sleep(1000);
      inc(i);
    end;
  if(UUT.AppliedPressure >442.0) or (UUT.AppliedPressure < 438.0) then
    begin
      cbHighPress.Checked := False;   //turn off high pressure
      cbVent.Checked := False;    //Allow to vent
      TH.SetFail(True);
      TH.SetPass(False);
      TH.SetTIP(False);
      ShowMessage('Pressure out of range:  '+FloatToStrF(UUT.AppliedPressure,ffFixed, 15,1));
      Exit;
    end
    else
      //Set up the timer to check pressure over 15 seconds and see if a leak occurs.
      begin
        Memo1.Lines.Add('Syringe successfully pressurized to ' + FloatToStrF(UUT.AppliedPressure,ffFixed, 15,1) + 'PSI.');
        Memo1.Lines.Add('Beginning 15 second leak decay test.');
        Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
        cbSeal.Checked := True;   //Seal the pressure in the syring
        cbHighPress.Checked := False; //Turn off High Pressure
        cbVent.Checked := False;  //Release the high pressure from system
        UUT.LeakTestStartPress := UUT.AppliedPressure;    //record starting pressure
        pbLeakTime.Visible := True;
        lblLeakRate.Visible := True;
        lblLeakRate.Caption := FloatToStrF(UUT.AppliedPressure,ffFixed, 15,1);
        lblDecayVal.Visible := True;
        tmrGP.Interval := CAL_LEAK_READ_PER;    //set pressure read interval
        UUT.LeakCnt := 0;     //initialize
        tmrGP.Enabled := True;
      end;
end;

(*******************************************************************************
 CalAutoCal: Run all steps necessary to autocalibrate the syringe
*******************************************************************************)
procedure TfrmMain.CalAutoCal;
begin
  LJ.FlushUART;         //make sure no remaining data in UART.
  LJ.PutBit(PTIP,1);          //turn on TEST IN PROGRESS lamp
  LJ.PutBit(PPASS,0);         //turn off pass lamp
  LJ.PutBit(PFAIL,0);         //turn off fail lamp
  sbStatus.Panels[0].Text := 'Executing auto-calibration routine.';
  Memo1.Clear;
  Memo1.Lines.Add('Executing auto-calibration routine.');
  AutoCal := True;
  pnlCal.Enabled := False;  //Disable all buttons until routine is complete.
  CalLeakTest;
end;

(*******************************************************************************
 calEnterCal: Configures RX state machine to look for CAL echo from UUT.
*******************************************************************************)
procedure TfrmMain.calEnterCal;
begin
  ResetUUT;
  LJ.FlushUART;
  UUT.SendCmd(csEnterCal, TIM_ENTERCAL, 3, [CAL_ENTERCAL1,CAL_ENTERCAL2]);
end;

(******************************************************************************
 CalSetSN: Set the serial number in the UUT
*******************************************************************************)
procedure TfrmMain.CalSetSN;
begin
  sbStatus.Panels[0].Text := 'Sending SN to UUT.';
  Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
  UUT.SNCnt := 0;
  UUT.WaitForSN := True;
  //Memo1.Lines.Add('SN TX> BD');
  UUT.SendCmd(csSendSN, TIM_STORE_SN, 3, [CAL_STORE_SN]);
end;

(*******************************************************************************
 CalReadSN: Reads the serial number back from the UUT
*******************************************************************************)
procedure TfrmMain.CalReadSN;
var
  val: byte;
begin
  UUT.SendCmd(csReadSN, TIM_SEND_SN, 3, [CAL_SEND_SN]);
end;

(*******************************************************************************
CalCaptureZero: Captures the A/D data measured with zero pressure applied,
                     to be used in A1Calc and C1Calc. Also used to capture
                     the RTZ (Return to Zero) pressure value. 
*******************************************************************************)
procedure TfrmMain.CalCaptureZero;
var
  i: Integer;
begin
  if UUT.CaptureZero then
      sbStatus.Panels[0].Text := 'Capturing Zero pressure values.'
      else if UUT.CaptureRtz then
         sbStatus.Panels[0].Text := 'Capturing Return to Zero pressure values.';
  Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
  cbFastest.Checked := True;
  cbVent.Checked := False;
  cbHighPress.Checked := False;
  Sleep(3000);    //allow pressure to drop
  i := 0;
  UUT.AppliedPressure := 10;  //sent to initial value to force loop to run.
  while((i<5) and (UUT.AppliedPressure <-1.0) or (UUT.AppliedPressure > 1.0)) do
    begin                                       
      GetUUTPressure;
      Sleep(1000);
      inc(i);
    end;
  if(UUT.AppliedPressure <-1.0) or (UUT.AppliedPressure > 1.0) then
    ShowMessage('Pressure out of range:  '+FloatToStrF(UUT.AppliedPressure,ffFixed, 15,3));
  ReadsCounter := 1;
  UUT.WaitForAd := True;
  UUT.NumReadsRequired := 15;
  for i := 0 to 27 do    //clear current values in the array
    UUT.ADArrayAvg[i] := 0;
  UUT.SendCmd(csCaptureAD, TIM_SEND_AD, 5, [CAL_SEND_AD]);
end;

(*******************************************************************************
CalCaptureMax: Captures the A/D data measured with Max pressure applied,
                     to be used in A1Calc and C1Calc
*******************************************************************************)
procedure TfrmMain.CalCaptureMax;
var
  i: Integer;
begin
  sbStatus.Panels[0].Text := 'Capturing Max pressure values.';
  cbFastest.Checked := True;
  cbVent.Checked := True;
  cbHighPress.Checked := True;
  Sleep(3000);    //need 3 seconds to pressurize
  i := 0;
  UUT.AppliedPressure := 0;  //sent to initial value to force loop to run.
  while((i<5) and ((UUT.AppliedPressure >441.0) or (UUT.AppliedPressure < 439.0))) do
    begin
      GetUUTPressure;
      Sleep(1000);
      inc(i);
    end;
  if(UUT.AppliedPressure >441.0) or (UUT.AppliedPressure < 439.0) then
    ShowMessage('Pressure out of range:  '+FloatToStrF(UUT.AppliedPressure,ffFixed, 15,3));
  //StopCal();
  UUT.PressMeasFullPress := UUT.AppliedPressure;    //save value for calculations
  ReadsCounter := 1;
  UUT.WaitForAd := True;
  UUT.NumReadsRequired := 15;
  UUT.CaptureMax := True;
  for i := 0 to 27 do    //clear current values in the array
    UUT.ADArrayAvg[i] := 0;
  UUT.SendCmd(csCaptureAD, TIM_SEND_AD, 5, [CAL_SEND_AD]);
end;

(*******************************************************************************
 CalSendParams: Send Parameters to the UUT
*******************************************************************************)
procedure TfrmMain.CalSendParams;
begin
  (*UUT.WaitForSendParams := True;
  UUT.RxIdx := 0;  //init to zero
  sbStatus.Panels[0].Text := 'Saving CAL values to UUT.';
  Application.ProcessMessages;*)

  //UUT.CalcADVals;
  UUT.SendCmd(csSendParams, TIM_SEND_PARAMS, 3, [CAL_SEND_PARAMS]);
end;

(*******************************************************************************
 CalExitCal: Causes UUT to exit CAL routine.
*******************************************************************************)
procedure TfrmMain.CalExitCal;
begin
  UUT.ExitCal1Done := False;    //initialize
  sbStatus.Panels[0].Text := 'Exiting Calibration Routine.';
  UUT.WaitForExitCal := True;
  UUT.SendCmd(csExitCal, TIM_EXIT, 3, [CAL_EXIT]);
end;


(*******************************************************************************
 ComTmrTimer: This timer fires after the amountof time specified by the respTime
              parameter of the SendCmd function. It then calls the appropriate
              procedure based on the UUT.CalSequence value.
*******************************************************************************)
procedure TfrmMain.ComTmrTimer(Sender: TObject);
begin
  //make sure the LJ rx buffer hasn't been read too many times (timed out)
  inc(LJ.RxRespCnt);
    if LJ.RxRespCnt > LJ.RxRespTries then
      begin
        ComTmr.Enabled := False;
        ShowMessage('Timer timed out.');
        LJ.RxRespCnt := 0;
      end
  //Now check the LJ rx buffer to see if the UUT has responded.
  else
  begin
    LJ.ReadUART;
    UpdateRx(LJ.RxData, LJ.iRxCnt);
    if LJ.iRxCnt > 0 then
      begin
        ComTmr.Enabled := False;    //kill the timer
        LJ.RxRespCnt := 0;          //zero the counter.
        case UUT.CalSequence of
          csLedOn,
          csLedOff,
          csLcdOn,
          csLcdOff: rxDispUtil;
          csEnterCal: rxEnterCal;
          csCaptureAD: rxCaptureAD;
          csSendSN: rxSendSN;
          csReadSN: rxReceiveSN;
          csSendParams: rxSendParams;
          csExitCal: rxExitCal;
        end;
    end;
  end;
end;

(*******************************************************************************
 rxEnterCal: Command UUT to enter cal mode. Exchange PW with UUT.
*******************************************************************************)
procedure TfrmMain.rxEnterCal;
begin
  if (LJ.iRxCnt = 1) and (LJ.RxData[LJ.iRxCnt-1] = CAL_READY) then
    UUT.SendCmd(csEnterCal, 5, 10, [CAL_PW1])
  else if LJ.RxData[LJ.iRxCnt-1] = CAL_PW1 then
    UUT.SendCmd(csEnterCal, 5, 10, [CAL_PW2])
  else if LJ.RxData[LJ.iRxCnt-1] = CAL_PW2 then
    UUT.SendCmd(csEnterCal, 5, 10, [CAL_PW3])
  else if LJ.RxData[LJ.iRxCnt-1] = CAL_PW3 then
    UUT.SendCmd(csEnterCal, 5, 10, [CAL_PW4])
  else if (LJ.RxData[0] = CAL_PW4) then
    if LJ.RxCnt > 1 then
      if LJ.RxData[LJ.iRxCnt-1] = CAL_READY then
        begin
          Memo1.Lines.Add('Calibration mode entered successfully!');
          if AutoCal then
            calSetSn;
         end
           else
             Memo1.Lines.Add('Failed to enter Calibration mode.');
end;

(*******************************************************************************
 rxCaptureAD: Obtain A/D data from the UUT via LJ com port.
*******************************************************************************)
procedure TfrmMain.rxCaptureAD;
var
  col, i: Integer;
begin
      //Now copy the A/D data from the RX buffer into the UUT object A/D data array, assuming CAL_READY was returned
  if (LJ.iRxCnt = SZ_AD_VALS) and (LJ.RxData[LJ.iRxCnt-1] = CAL_READY) then
    begin
      ComTmr.Enabled := False;    //kill the timer
      LJ.RxRespCnt := 0;
      //Figure out which column of the A/D data grid the resultant data should go in
      if UUT.CaptureZero then   //0 PSI data stored in column 1 of ADData grid
        col := 1
        else
        if UUT.CaptureMax then  //Max PSI data stored in column 2 of ADData grid
          col := 2
          else
          if UUT.CaptureRtz then   //Return to 0 PSI data stored in column 3 of ADData grid
          col := 3;
      for i := 1 to 28 do
        UUT.ADArray[i] := LJ.RxData[i];
      //Read the data multiple times and take average values, per Test Spec
      if ReadsCounter <= UUT.NumReadsRequired then
        begin
           Memo1.Lines.Add ('AD< '+UUT.ProcessADData(sgADData,col)+
                            'Count: '+IntToStr(ReadsCounter));
           for i := 0 to 27 do    //Add data values to the values in the averaging array
             UUT.ADArrayAvg[i] := UUT.ADArrayAvg[i]+ UUT.ADArray[i];
           inc(ReadsCounter);     //controls how many reads of A/D data to do
           if ReadsCounter <=UUT.NumReadsRequired then
             begin
               UUT.SendCmd(csCaptureAD, TIM_SEND_AD, 5, [CAL_SEND_AD]);
             end   //Request next read and exit the routine.
             //Number of required reads are complete. Now process the accumulated data.
             else
               begin
                 //Handle capturing of max pressure values average (from 15 reads)
                 if UUT.CaptureMax then
                   begin
                     adCaptureMax;
                     UUT.WaitForAD := False;   //done, set to false
                     if AutoCal then
                       begin
                         UUT.CaptureRtz := True;   //must set this here, as CalCaptureZero is shared with RTZ.
                         CalCaptureZero;
                       end;
                   end
                 //Handle capturing  Zero pressure values average (from 15 reads)
                 else
                 if UUT.CaptureZero then
                   begin
                     adCaptureZero;
                     UUT.WaitForAD := False;   //done, set to false
                     if AutoCal then
                        begin
                          CalCaptureMax;
                        end;
                   end
                 else
                 if UUT.CaptureRtZ then
                   begin
                     adCaptureRtZ;
                     UUT.WaitForAD := False;   //done, set to false
                     if AutoCal then
                        CalSendParams;
                   end
                 else
                 if UUT.CaptureOnce then
                   begin
                     adCaptureOnce;
                   end;

               end;

         end;    //end if ReadsCounter < UUT.NumReadsRequired
    end  //end if Length(tmp)......
    else
      Memo1.Lines.Add('Attempt # '+ IntToStr(LJ.RxRespCnt));
end;

(*******************************************************************************
 rxSendSN: Compute SN and send to UUT via LJ comport.
*******************************************************************************)
procedure TfrmMain.rxSendSN;
begin
  //When Send SN is first called, must build SN and prepare to store.
  if (UUT.SNCnt = 0) and (LJ.RxData[0] = CAL_STORE_SN)  then
    begin
      UUT.BuildSN;
      UUT.SNCnt := 1;
      Memo1.Lines.Add(
        'bSN[1]:' + IntToHex(byte(UUT.bSNsent[1]), 0) + #13 +
        'bSN[2]:' + IntToHex(byte(UUT.bSNsent[2]), 0) + #13 +
        'bSN[3]:' + IntToHex(byte(UUT.bSNsent[3]), 0) + #13 +
        'bSN[4]:' + IntToHex(byte(UUT.bSNsent[4]), 0) + #13 +
        'bSN[5]:' + IntToHex(byte(UUT.bSNsent[5]), 0) + #13 +
        'bSN[6]:' + IntToHex(byte(UUT.bSNsent[6]), 0) + #13 +
        'bSN[7]:' + IntToHex(byte(UUT.bSNsent[7]), 0) + #13 +
        'LSB of sum: ' + IntToHex(byte(UUT.bSum), 0)
        );
        UUT.SendCmd(csSendSN, TIM_STORE_SN, 3, [UUT.bSNsent[UUT.SNCnt]]);
    end
    else if (LJ.RxData[0] = UUT.bSNsent[UUT.SNCnt]) and (UUT.SNCnt <8) then
      begin
        inc(UUT.SNCnt);
        if UUT.SNCnt <= 7 then
           begin
             UUT.SendCmd(csSendSN, TIM_STORE_SN, 3, [UUT.bSNsent[UUT.SNCnt]]);
           end
           else
             begin
               UUT.SendCmd(csSendSN, TIM_STORE_SN, 3, [UUT.bSNsent[UUT.bSum]]);
             end;
      end
      else if (UUT.SNCnt > 7) and (LJ.RxData[1] = CAL_READY) then
        begin
           Memo1.Lines.Add('SN TX< UUT received SN successfully.');
           Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
           UUT.WaitForSN := False;
          if AutoCal then
             CalReadSN;
        end;
 end;   //end if iRxCnt > 0


 (*******************************************************************************
 rxRecieveSN: Get the stored SN in the UUT
*******************************************************************************)
procedure TfrmMain.rxReceiveSN;
var
  i: Integer;
begin
  UpdateRx(LJ.RxData, LJ.iRxCnt);
  if LJ.iRxCnt>0 then
     begin
        ComTmr.Enabled := False;
        LJ.RxRespCnt := 0;
       if LJ.RxData[LJ.iRxCnt-1] = CAL_READY then
       begin
         for i := 1 to 18 do
           UUT.bSNRcv[i] := LJ.RxData[i];
         Memo1.Lines.Add('SN RX< ' + UUT.ProcessSNData(sgSN));
         Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
         UUT.RcvSNFromUUT := False;
         if AutoCal then
            begin
              UUT.CaptureZero := True;  //must set here, as CalCaptureZero is shared with RTZ.
              CalCaptureZero;
            end;
       end;
     end;  //end if RxCnt>0
end;

(*******************************************************************************
 rxSendParams: Send calculated calbration params to the UUT.
*******************************************************************************)
procedure TfrmMain.rxSendParams;
begin
  ComTmr.Enabled := False;    //kill the timer
  LJ.RxRespCnt := 0;          //reset the received response counter
  if (LJ.RxData[LJ.iRxCnt-1] = CAL_SEND_PARAMS) and  (UUT.RxIdx = 0)  then
    begin
      UUT.SendCmd(csSendParams, TIM_SEND_PARAMS, 3, [UUT.bParams[UUT.RxIdx]]);
    end
  else if (LJ.RxData[LJ.iRxCnt-1]=UUT.bParams[UUT.RxIdx]) and (UUT.RxIdx < SZ_TOTAL_PARAMS) then
    begin
      inc(UUT.RxIdx);
      if UUT.RxIdx <= SZ_TOTAL_PARAMS then
         UUT.SendCmd(csSendParams, TIM_SEND_PARAMS, 3, [UUT.bParams[UUT.RxIdx]]);
    end
  else if (UUT.RxIdx >= SZ_SEND_PARAMS) and (LJ.iRxCnt > 1) then
    if LJ.RxData[LJ.iRxCnt-1] = CAL_READY then
      begin
        sbStatus.Panels[0].Text := 'Parameters successfully stored.';
        Memo1.Lines.Add('STOR PARAMS< Parameters Stored.');
        Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
        if AutoCal then
           begin
             CalExitCal;
             pnlCal.Enabled := True;
           end;
      end;
end;

(*******************************************************************************
 rxExitCal: Instruct the UUT to exit calibration mode and shut down.
*******************************************************************************)
procedure TfrmMain.rxExitCal;
begin
   if LJ.iRxCnt > 2 then
   begin
   if (LJ.RxData[1] = CAL_READY) and (LJ.RxData[2] = CAL_READY) then
     begin
       LJ.PutBit(PPASS,1);
       Memo1.Lines.Add('Exit Cal<  Cal complete and exiting.');
       Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
       sbStatus.Panels[0].Text := 'Calibration complete.';
     end
   end
     else
       begin
          LJ.PutBit(PFAIL,1);
          Memo1.Lines.Add('Exit Cal<  Cal complete and exiting.');
          Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
          sbStatus.Panels[0].Text := 'Calibration Failed.';
       end;
     LJ.PutBit(PTIP,0);    //Turn off TEST IN PROGRESS lamp
     AutoCal := False;
     UUT.WaitForExitCal := False;
end;

(*******************************************************************************
 rxDispUtil: Handle responsed from UUT when commanded to do LCD/LED display utils
*******************************************************************************)
procedure TfrmMain.rxDispUtil;
begin
  if (LJ.RxData[LJ.iRxCnt-1] = CAL_READY) then
  begin
    case UUT.CalSequence of
      csLedOn: Memo1.Lines.Add('LED ON.');
      csLedOff: Memo1.Lines.Add('LED OFF.');
      csLcdOn: Memo1.Lines.Add('LCD- all segments on.');
      csLcdOff: Memo1.Lines.Add('LCD- all segments off.');
    end;
  end;

end;

end.
